﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class CNetDataExchange
{
    public enum NetEventClientState
    {
        NET_DEVICE_OPEN = 0,
        NET_DEVICE_CLOSE = 1,
        NET_DEVICE_CONNECTING
    }
    public delegate void DelegateEventClientState(CNetDataExchange sender, NetEventClientState state);
    public event DelegateEventClientState EventClientClose;
    public void ClientState(CNetDataExchange sender, NetEventClientState state)
    {
        m_connectState = state;
        if (EventClientClose != null)
        {
            EventClientClose(sender, state);
        }

    }
    private NetEventClientState m_connectState = NetEventClientState.NET_DEVICE_CLOSE;
    private string m_strIP;
    private int m_iPort;
    public CNetDataExchange(string strIP, int iPort)
    {
        m_strIP = strIP;
        m_iPort = iPort;
        Thread t = new Thread(new ThreadStart(Init));
        t.Start();
    }

    void Init()
    {
        string strIP = m_strIP;
        int iPort = m_iPort;
        int iWaitTime = 10000;
        while (true)
        {
            if(m_connectState == NetEventClientState.NET_DEVICE_CLOSE)
            {
                TryConnect(strIP, iPort, iWaitTime);
            }
          
            Thread.Sleep(iWaitTime);
        }
    }

    private static bool IsConnectionSuccessful = false;
    private static Exception socketexception;
    private static ManualResetEvent TimeoutObject = new ManualResetEvent(false);
    private static void CallBackMethod(IAsyncResult asyncresult)
    {
        try
        {
            IsConnectionSuccessful = false;
            TcpClient tcpclient = asyncresult.AsyncState as TcpClient;
            if (tcpclient.Client != null)
            {
                tcpclient.EndConnect(asyncresult);
                IsConnectionSuccessful = true;
            }
        }
        catch (Exception ex)
        {
            IsConnectionSuccessful = false;
            socketexception = ex;
        }
        finally
        {
            TimeoutObject.Set();
        }
    }
    void TryConnect(string strIP, int iPort, int iWaitTime)
    {
        if(m_connectState == NetEventClientState.NET_DEVICE_CLOSE)
        {
            try
            {
                TimeoutObject.Reset();
                socketexception = null;
                if (m_client == null )m_client = new TcpClient();

                ClientState(this, NetEventClientState.NET_DEVICE_CONNECTING);

                m_client.BeginConnect(strIP, iPort, new AsyncCallback(CallBackMethod), m_client);

                if (TimeoutObject.WaitOne(iWaitTime, false))
                {
                    if (!IsConnectionSuccessful)
                    {
                        throw socketexception;
                    }
                }
                else
                {
                    m_client.Close();
                    throw new TimeoutException("TimeOut Exception");
                }
                if (m_client.Connected)
                {
                    m_StreamToClient = m_client.GetStream();
                    AsyncCallback callBack = new AsyncCallback(ReadHead);

                    m_iNeedReadNum = NetCommandDefine.GetHeadLength();
                    m_iReadNum = 0;
                    m_StreamToClient.BeginRead(m_RevicectBuffer, 0, m_iNeedReadNum, callBack, null);
                    ClientState(this, NetEventClientState.NET_DEVICE_OPEN);
                }
            }
            catch (System.Exception ex)
            {
                ClientState(this, NetEventClientState.NET_DEVICE_CLOSE);
                string str = ex.Message;
             //   System.Diagnostics.Debug.Assert(false);
            }
        }
       

    }

    TcpClient m_client ;

    NetworkStream m_StreamToClient;
    byte[] m_RevicectBuffer = new byte[1024];
    int m_iReadNum, m_iNeedReadNum;

    bool m_isClientOpen = true;
    public void Close()
    {
        ClearSendBuffer();
        if (m_isClientOpen == true)
        {
            m_isClientOpen = false;
            // m_client.Client.Shutdown(SocketShutdown.Both);
            m_StreamToClient.Close();
            m_client.Client.Close();
            m_client.Close();
            m_client = null;
            ClientState(this, NetEventClientState.NET_DEVICE_CLOSE);
        }


    }
    void ClearSendBuffer()
    {
        m_SendDataLock.WaitOne();
        m_SendBuffer.Clear();
        m_SendDataLock.ReleaseMutex();
    }
    private void ReadHead(IAsyncResult ar)
    {
        try
        {
            int iHaveReadNum;
            lock (m_StreamToClient)
            {
                iHaveReadNum = m_StreamToClient.EndRead(ar);
            }
            m_iNeedReadNum -= iHaveReadNum;
            m_iReadNum += iHaveReadNum;
            if (m_iNeedReadNum > 0)
            {

                lock (m_StreamToClient)
                {
                    AsyncCallback callBack = new AsyncCallback(ReadHead);
                    m_StreamToClient.BeginRead(m_RevicectBuffer, m_iReadNum, m_iNeedReadNum, callBack, null);
                }
            }
            else
            {
                m_iNeedReadNum = (int)NetCommandDefine.GetDataLength(ref m_RevicectBuffer);
                m_iNeedReadNum += NetCommandDefine.GetTailLength();
                m_pCurrentReveiceBuffer = new byte[m_iNeedReadNum + NetCommandDefine.GetHeadLength()];
                Array.Copy(m_RevicectBuffer, 0, m_pCurrentReveiceBuffer, 0, m_iReadNum);

                AsyncCallback callBack = new AsyncCallback(ReadComplete);
                m_StreamToClient.BeginRead(m_pCurrentReveiceBuffer, m_iReadNum, m_iNeedReadNum, callBack, null);

            }
        }
        catch (System.Exception ex)
        {
            string str = ex.Message;
            Close();
        }
    }
    // 再读取完成时进行回调
    //iSegmentIndex 总段号，段号
    private void ReadComplete(IAsyncResult ar)
    {
        try
        {
            int iHaveReadNum;
            lock (m_StreamToClient)
            {
                iHaveReadNum = m_StreamToClient.EndRead(ar);
            }
            m_iNeedReadNum -= iHaveReadNum;
            m_iReadNum += iHaveReadNum;
            if (m_iNeedReadNum > 0)
            {
                AsyncCallback callBack = new AsyncCallback(ReadComplete);
                m_StreamToClient.BeginRead(m_pCurrentReveiceBuffer, m_iReadNum, m_iNeedReadNum, callBack, null);
            }
            else
            {
                //to do
                //提交数据
                m_dataCommit.AddData(m_pCurrentReveiceBuffer);
                // 清空缓存，避免脏读
                m_iReadNum = 0;
                m_iNeedReadNum = NetCommandDefine.GetHeadLength();
                AsyncCallback callBack = new AsyncCallback(ReadHead);
                m_StreamToClient.BeginRead(m_RevicectBuffer, 0, m_iNeedReadNum, callBack, null);

            }
        }
        catch (System.Exception ex)
        {
            string str = ex.Message;
            Close();

        }
    }

    byte[] m_pCurrentReveiceBuffer;
    public void PushNewData(byte[] sendData)
    {
        //      System.Diagnostics.Debug.Assert(sendData[15] != 17);
        m_SendDataLock.WaitOne();
         m_SendBuffer.Add(sendData);
        m_SendDataLock.ReleaseMutex();
        StartWrite();
    }

    int GetSendNum()
    {
        int ret;
        m_SendDataLock.WaitOne();
        ret = m_SendBuffer.Count;
        m_SendDataLock.ReleaseMutex();
        return ret;
    }
    int m_isWriteing = 0;
    bool IsWriteing()
    {
        int iRet = 0;
        Interlocked.Exchange(ref iRet, m_isWriteing);
        return iRet == 1 ? true : false;
    }
    void SetWrite(bool IsWrite)
    {
        Interlocked.Exchange(ref m_isWriteing, IsWrite == true ? 1 : 0);
    }


    bool GetFirstDataPrepareSend(out byte[] retData)
    {
        retData = null;
        bool ret = false;
        m_SendDataLock.WaitOne();
        {
            if (m_SendBuffer.Count > 0)
            {
                ret = true;
                retData = m_SendBuffer.First();
                m_SendBuffer.RemoveAt(0);
            }
        }
        m_SendDataLock.ReleaseMutex();
        return ret;
    }
    void StartWrite()
    {
        try
        {
            if (IsWriteing() == false && m_client.Connected == true)
            {
                WriteLatestDataToNet();
            }
            else
            {

            }
        }
        catch (System.Exception ex)
        {
            string str = ex.Message;
            Close();
        }

    }
    void WriteLatestDataToNet()
    {
        byte[] pData;
        if (GetFirstDataPrepareSend(out pData) == true)
        {
            SetWrite(true);
            AsyncCallback callBack = new AsyncCallback(WriteData);
            m_StreamToClient.BeginWrite(pData, 0, pData.Length, callBack, null);
        }
        else
        {
            SetWrite(false);
        }

    }
    void WriteData(IAsyncResult ar)
    {
        try
        {
            lock (m_StreamToClient)
            {
                m_StreamToClient.EndWrite(ar);
            }
            if (GetSendNum() > 0)
            {
                WriteLatestDataToNet();
            }
            else
            {
                SetWrite(false);
            }

        }
        catch (System.Exception ex)
        {
           string str =  ex.Message;

            Close();

        }
    }

    Mutex m_SendDataLock = new Mutex();
    //优先级<数据队列<<等待时间，数据>>>
    List<byte[]> m_SendBuffer = new List<byte[]>();

    static private CNetDataExchange m_pExchange;
    static public CNetDataExchange GetInstance()
    {
        if(m_pExchange == null)
        {
            m_pExchange = new CNetDataExchange("127.0.0.1", 10086);
        }
        return m_pExchange;
    }
    private NetDataHander m_dataCommit = new NetDataHander();
}
